package at.ltasat.train.set;

import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Random;

import at.ltasat.ArgumentClazzData;
import at.ltasat.ArgumentClazzDataClazzManagerIO;
import at.ltasat.CnfFeaturesFactory;
import at.ltasat.classifier.Classification;
import at.ltasat.classifier.Classifier;
import at.ltasat.classifier.ClazzManager;
import at.ltasat.classifier.Membership;
import at.ltasat.classifier.ObjectFeatures;
import at.ltasat.classifier.io.ClazzManagerIO;
import at.tasat.io.OutputFormatter;
import at.tasat.parser.DimacsParser;

public class TrainingSetMain {

	public static void main(String[] args) {
		TrainingSetConfiguration config = TrainingSetConfiguration.get(args);
		int result = main(config);
		System.exit(result);
	}

	public static int main(TrainingSetConfiguration config) {
		final boolean verbose = config.isVerboseStatus();
		final PrintStream out = new PrintStream(config.getOut());
		try {
			if (config.isVerboseConfig()) {
				out.println(OutputFormatter.getCommentString(config.toString()));
			}
			final ClazzManagerIO<ArgumentClazzData> clazzManagerIO = new ArgumentClazzDataClazzManagerIO();
			final ClazzManager<ArgumentClazzData> clazzManager;
			final TrainingSet<ArgumentClazzData> set;
			if (config.getInFile().exists()) {
				log(verbose, out, "reading training set from %s", config.getInFile().getAbsolutePath());
				log(verbose, out, "specified classes will be ignored");
				clazzManager = clazzManagerIO.read(config.getIn());
				set = TrainingSet.read(config.getIn(), clazzManager);
				config.getIn().close();
			} else {
				log(verbose, out, "creating training set");
				clazzManager = new ClazzManager<ArgumentClazzData>(config.getClazzes());
				set = new TrainingSet<ArgumentClazzData>(CnfFeaturesFactory.INSTANCE.getNumberOfFeatures(), new ClazzManager<ArgumentClazzData>(config.getClazzes()));
			}
			log(verbose, out, "starting to fill training set");
			final Classifier<byte[], ArgumentClazzData> classifier = new ManualClassifierFactory(config.getIterations(), config.getMaxTime()).createTrainer(clazzManager, null).create();
			// TODO iterate over object space rather than randomly probing
			final Random random = new Random();
			for (int count = 0; count < config.getSize(); count++) {
				final int nVariables = random.nextInt(config.getVariables()) + 1;
				final int nClauses = random.nextInt(config.getClauses()) + 1;
				log(verbose, out, "data #%d (%d,%d)", count, nVariables, nClauses);
				final byte[] cnf = DimacsFuzzer.getRandomCnf(nVariables, nClauses).getBytes();
				final Classification<byte[], ArgumentClazzData> classification = classifier.classify(cnf);
				final ObjectFeatures features = CnfFeaturesFactory.INSTANCE.create(DimacsParser.parse(new ByteArrayInputStream(cnf)));
				log(verbose, out, "object: %s", features);
				for (Membership<ArgumentClazzData> membership : classification) {
					int degree = (int) (membership.getDegree() * config.getIterations());
					for (int i = 0; i < degree; i++) {
						set.add(features, membership.getClazz());
					}
				}
				set.add(features, classification.getDominantClazz());
			}
			log(verbose, out, "finished to fill training set");
			log(verbose, out, "writing training set to %s", config.getInFile().getAbsolutePath());
			final OutputStream result = new FileOutputStream(config.getInFile());
			clazzManagerIO.write(result, classifier.getClazzManager());
			set.write(result);
			return 0;
		} catch (Throwable error) {
			(new PrintStream(config.getErr())).println(OutputFormatter.getErrorString(error.toString()));
			return 1;
		}
	}

	private static void log(boolean verbose, PrintStream out, String msg, Object... args) {
		if (verbose) {
			out.println(OutputFormatter.getCommentString(String.format(msg, args)));
		}
	}

}
